﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WordNet.Core;
using WordNet.Core.DataContract;
using WordNet.Core.ElementContracts;

namespace DataFileProvider
{
    public class WordNetFile: IDataSource
    {
        private readonly FileInfo _file;
        protected Encoding _enc = Encoding.Default;

        public WordNetFile(FileInfo file)
        {
            if ( file == null)
                throw new ArgumentNullException();

            _file = file;
            _enc = DetectEncoding();
        }

        private Encoding DetectEncoding()
        {
            Encoding enc = Encoding.Default;
            using (StreamReader encDetector = new StreamReader(_file.FullName, true))
            {
                enc = encDetector.CurrentEncoding;
            }
            return enc;
        }
        
        private int Compare (string key, BinaryReader reader)
        {
            if (key == null || key.Length == 0)
               throw new ArgumentException();
            if (reader == null)
                throw new ArgumentException();
            List<byte> lineData = new List<byte>(100);
          
            while(reader.PeekChar() != ' ')
            {
                lineData.Add(reader.ReadByte());
            }
           
            string readData = _enc.GetString(lineData.ToArray());

            return String.CompareOrdinal(readData, key);  
        }

        public string Search(string word)
        {
            String line = null;
            int keyLength = _enc.GetByteCount(word);
            using (BinaryReader buffer = new BinaryReader(_file.OpenRead(), _enc))
            {
                long start = 0;
                long midpoint = -1;
                long stop = buffer.BaseStream.Length;
                int cmp;

                while (stop - start > 1)
                {
                    midpoint = (start + stop) / 2;
                    buffer.BaseStream.Seek(midpoint, SeekOrigin.Begin);
                    RewindToLineStart(buffer);

                    cmp = Compare(word, buffer);
                    if (cmp == 0)
                    {
                        long offset = buffer.BaseStream.Position - keyLength;
                        line = ReadLine(offset);
                        break;
                    }
                    if (cmp > 0)
                        stop = midpoint;
                    else
                        start = midpoint;
                }
            }
           return line;
        }

        public IEnumerable<string> AllLines()
        {
            const int FirstLine = 29;
            int skip = 0;
            if (_file.Name.StartsWith("data") || _file.Name.StartsWith("index"))
                skip = FirstLine;

            return File.ReadLines(_file.FullName).Skip(skip);
        }

        public string Name
        {
            get { return _file.Name; }
        }

        public string ReadLine(long key)
        {
            string line = string.Empty;
            using (StreamReader reader = new StreamReader(_file.FullName, _enc))
            {
                reader.BaseStream.Seek(key, SeekOrigin.Begin);
                line = reader.ReadLine();
                reader.Close();
            }
            return line;
        }

        public static void RewindToLineStart(BinaryReader buf)
        {
            long i = buf.BaseStream.Position;

                buf.BaseStream.Position = i - 1;
                //buf.Position = i - 1;
                if (buf.ReadChar() == '\r' && buf.ReadChar() == '\n')
                    i--;
                if (i > 0)
                    i--;
                char c;
                for (; i > 0; i--)
                {
                    buf.BaseStream.Seek(i, SeekOrigin.Begin);
                    c = buf.ReadChar();
                    if (c == '\n' || c == '\r')
                    {
                        i++;
                        break;
                    }
                }
            }
        }
    }

